<template>
  <view :style="[imageContainerStyle]" class="sc-image" @tap="clickHandler">
    <!-- 图片元素 -->
    <image v-if="showImage" :fade-show="fadeShow" :lazy-load="lazyLoad" :mode="mode"
      :show-menu-by-longpress="showMenuByLongpress" :src="currentSrc" :style="[imageStyle]" :webp="webp"
      class="sc-image__img" @error="handleError" @load="handleLoad" />
    <!-- 加载中占位符 -->
    <view v-if="isLoading && !isError" :style="[placeholderStyle]" class="sc-image__placeholder">
      <slot name="loading">
        <view class="sc-image__loading">
          <text class="sc-image__loading-text">{{ loadingText }}</text>
        </view>
      </slot>
    </view>

    <!-- 错误占位符 -->
    <view v-if="isError && !errorImage" :style="[placeholderStyle]" class="sc-image__error">
      <slot name="error">
        <view class="sc-image__error-content">
          <text class="sc-image__error-text">{{ errorText }}</text>
        </view>
      </slot>
    </view>

    <!-- 遮罩层 -->
    <view v-if="showMask" :style="[maskStyle]" class="sc-image__mask">
      <slot name="mask"></slot>
    </view>
  </view>
</template>

<script>
import { marginProps, marginStyle } from "../../libs/props/margin.js";
import { positionProps, positionStyle } from "../../libs/props/position";
import mpMixin from "../../libs/mixin/mpMixin.js";
import mixin from "../../libs/mixin/mixin.js";

/**
 * @component sc-image 图片组件
 * @property {String}        src                - 图片资源地址，必填项
 * @property {String}        mode               - 图片裁剪、缩放的模式，可选值参考 validModes，默认 'aspectFill'
 * @property {String|Number} width              - 图片宽度，支持 px、rpx 等单位，默认 '320px'
 * @property {String|Number} height             - 图片高度，支持 px、rpx 等单位，默认 '240px'
 * @property {String|Number} round              - 图片圆角大小，默认 0
 * @property {String|Number} rotate             - 图片旋转角度，默认 0
 * @property {Boolean}       circle             - 是否显示为圆形图片，默认 false
 * @property {Boolean}       lazyLoad           - 是否开启图片懒加载，默认 true
 * @property {Boolean}       fadeShow           - 是否开启图片淡入显示，默认 true
 * @property {Boolean}       webp               - 是否尝试将图片转换为 webp 格式，默认 false
 * @property {Boolean}       showMenuByLongpress- 是否开启长按图片显示菜单，默认 false
 * @property {String}        loadingText        - 加载中显示的文本，默认 '↻'
 * @property {String}        errorText          - 加载失败显示的文本，默认 '!'
 * @property {String}        placeholder        - 图片占位图的资源地址
 * @property {String}        errorImage         - 加载失败时显示的替代图片地址
 * @property {Boolean}       showMask           - 是否显示遮罩层，默认 false
 * @property {String}        maskColor          - 遮罩层的颜色，默认 'rgba(0,0,0,0.3)'
 * @property {Boolean}       preview            - 是否开启图片预览功能，默认 false
 * @property {Array}         previewList        - 预览图片列表，默认 []
 * @property {Number}        previewCurrent     - 当前预览图片的索引，默认 0
 * @property {String}        bgColor            - 图片背景颜色
 * @property {Object}        customStyle        - 自定义样式对象，默认 {}
 * 
 * @event {Function} click - 点击图片容器事件
 * @event {Function} load - 图片加载完成事件
 * @event {Function} error - 图片加载失败事件
 */

export default {
  name: "ScImage",
  mixins: [mpMixin, mixin],
  props: {
    src: {
      type: String,
      required: true,
    },
    mode: {
      type: String,
      default: "aspectFill",
      validator: (value) => {
        const validModes = [
          "scaleToFill",
          "aspectFit",
          "aspectFill",
          "widthFix",
          "heightFix",
          "top",
          "bottom",
          "center",
          "left",
          "right",
          "top left",
          "top right",
          "bottom left",
          "bottom right",
        ];
        return validModes.includes(value);
      },
    },
    width: {
      type: [String, Number],
      default: "320px",
    },
    height: {
      type: [String, Number],
      default: "240px",
    },
    round: {
      type: [String, Number],
      default: 0,
    },
    rotate: {
      type: [String, Number],
      default: 0,
    },
    duration: {
      type: [String, Number],
      default: 0,
    },
    circle: {
      type: Boolean,
      default: false,
    },
    lazyLoad: {
      type: Boolean,
      default: true,
    },
    fadeShow: {
      type: Boolean,
      default: true,
    },
    webp: {
      type: Boolean,
      default: false,
    },
    showMenuByLongpress: {
      type: Boolean,
      default: false,
    },
    loadingText: {
      type: String,
      default: "↻",
    },
    errorText: {
      type: String,
      default: "!",
    },
    placeholder: {
      type: String,
    },
    errorImage: {
      type: String,
    },
    showMask: {
      type: Boolean,
      default: false,
    },
    maskColor: {
      type: String,
      default: "rgba(0,0,0,0.3)",
    },
    preview: {
      type: Boolean,
      default: false,
    },
    previewList: {
      type: Array,
      default: () => [],
    },
    previewCurrent: {
      type: Number,
      default: 0,
    },
    bgColor: {
      type: String,
    },
    customStyle: {
      type: Object,
      default: () => ({}),
    },
    ...marginProps,
    ...positionProps,
  },
  emits: ["click", "load", "error"],
  data() {
    return {
      isLoading: true,
      isError: false
    };
  },
  computed: {
    /**
     * 获取当前显示的图片源
     * @returns {string} 返回当前应该显示的图片地址
     */
    currentSrc() {
      if (this.isError && this.errorImage) {
        return this.errorImage;
      }
      if (this.isLoading && this.placeholder) {
        return this.placeholder;
      }
      return this.src;
    },

    /**
     * 判断是否显示图片
     * @returns {boolean} 返回是否应该显示图片
     */
    showImage() {
      if (!this.isError) {
        return true;
      }
      if (this.isError && this.errorImage) {
        return true;
      }
      return false;
    },

    /**
     * 计算图片容器的样式
     * @returns {Object} 返回图片容器的样式对象
     */
    imageContainerStyle() {
      const baseStyle = {
        width: this.width && this.$sc.addUnit(this.width),
        height: this.height && this.$sc.addUnit(this.height),
        borderRadius: this.circle
          ? "50%"
          : this.round && this.$sc.addUnit(this.round),
        backgroundColor: this.bgColor && this.$sc.getColor(this.bgColor),
        overflow: "hidden",
        position: "relative",
        transform: `rotate(${this.rotate}deg)`,
        transition: `transform ${this.duration}s ease-in-out`
      };
      return this.$sc.deepMerge(baseStyle, marginStyle(this), positionStyle(this), this.customStyle);
    },

    /**
     * 计算图片元素的样式
     * @returns {Object} 返回图片元素的样式对象
     */
    imageStyle() {
      return {
        width: "100%",
        height: "100%",
        display: "block"
      };
    },

    /**
     * 计算占位符的样式
     * @returns {Object} 返回占位符的样式对象
     */
    placeholderStyle() {
      return {
        width: "100%",
        height: "100%",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        position: "absolute",
        top: 0,
        left: 0,
        backgroundColor: this.bgColor
          ? this.$sc.getColor(this.bgColor)
          : "#f5f5f5"
      };
    },

    /**
     * 计算遮罩层的样式
     * @returns {Object} 返回遮罩层的样式对象
     */
    maskStyle() {
      return {
        width: "100%",
        height: "100%",
        position: "absolute",
        top: 0,
        left: 0,
        backgroundColor: this.maskColor,
        display: "flex",
        alignItems: "center",
        justifyContent: "center"
      };
    }
  },
  mounted() {
    this.loadImage();
  },
  watch: {
    src: {
      handler() {
        this.loadImage();
      },
      immediate: false
    }
  },
  methods: {
    /**
     * 加载图片
     * 初始化图片加载状态,设置加载中和错误状态
     */
    loadImage() {
      if (!this.src) {
        this.isLoading = false;
        this.isError = true;
        return;
      }
      this.isLoading = true;
      this.isError = false;
    },

    /**
     * 图片加载完成处理函数
     * @param {Object} e - 加载完成事件对象
     * @emits load - 图片加载完成事件
     */
    handleLoad(e) {
      if (this.isError && this.errorImage) return;
      this.isLoading = false;
      this.isError = false;
      this.$emit("load", e);
    },

    /**
     * 图片加载失败处理函数
     * @param {Object} e - 加载失败事件对象
     * @emits error - 图片加载失败事件
     */
    handleError(e) {
      this.isLoading = false;
      this.isError = true;
      this.$emit("error", e);
    },

    /**
     * 图片点击处理函数
     * 支持预览模式和普通点击事件
     * @emits click - 图片点击事件
     */
    clickHandler() {
      if (this.preview && this.previewList.length > 0) {
        uni.previewImage({
          current: this.previewCurrent,
          urls: this.previewList
        });
      } else if (this.preview) {
        uni.previewImage({
          current: this.previewCurrent,
          urls: [this.src]
        });
      } else {
        this.$emit("click");
      }
    }
  }
};
</script>

<style lang="scss" scoped>
.sc-image {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  flex-shrink: 0;

  &__img {
    display: block;
    width: 100%;
    height: 100%;
  }

  &__placeholder,
  &__error {
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    background-color: #f5f5f5;
  }

  &__loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }

  &__loading-text {
    color: #999;
    font-size: 24rpx;
  }

  &__error {
    background-color: transparent;
    cursor: pointer;
    transition: background-color 0.2s;

    &:hover {
      background-color: #eeeeee;
    }
  }

  &__error-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }

  &__error-text {
    margin-bottom: 5rpx;
    color: #999;
    font-size: 24rpx;
  }

  &__mask {
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
  }
}
</style>
